package com.fzhucc.modules.system.rest;

import cn.hutool.core.collection.CollectionUtil;
import com.fzhucc.annotation.Log;
import com.fzhucc.annotation.rest.AnonymousGetMapping;
import com.fzhucc.annotation.rest.AnonymousPostMapping;
import com.fzhucc.config.RsaProperties;
import com.fzhucc.exception.BadRequestException;
import com.fzhucc.modules.security.domain.dto.AuthNewRegistUserDto;
import com.fzhucc.modules.security.domain.dto.AuthRegistUserDto;
import com.fzhucc.modules.sms.SmsRandomCodeService;
import com.fzhucc.modules.sms.SmsRegisterRandomCodeService;
import com.fzhucc.modules.system.domain.Dept;
import com.fzhucc.modules.system.domain.Role;
import com.fzhucc.modules.system.domain.User;
import com.fzhucc.modules.system.domain.UserSmall;
import com.fzhucc.modules.system.domain.dto.*;
import com.fzhucc.modules.system.domain.vo.UserPassVo;
import com.fzhucc.modules.system.domain.vo.UserPassWithOutOldVo;
import com.fzhucc.modules.system.service.*;
import com.fzhucc.modules.system.service.dto.RoleSmallDto;
import com.fzhucc.modules.system.service.dto.UserDto;
import com.fzhucc.modules.system.service.dto.UserOrgDto;
import com.fzhucc.modules.system.service.dto.UserQueryCriteria;
import com.fzhucc.utils.*;
import com.fzhucc.utils.enums.CodeEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Api(tags = "系统：用户管理")
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {

    private final PasswordEncoder passwordEncoder;
    private final UserService userService;
    private final DataService dataService;
    private final DeptService deptService;
    private final RoleService roleService;

    private final RedisUtils redisUtils;
    private final VerifyService verificationCodeService;

    private final SmsRandomCodeService smsRandomCodeService;

    private final SmsRegisterRandomCodeService smsRegisterRandomCodeService;

    @ApiOperation("导出用户数据")
    @GetMapping(value = "/download")
    @PreAuthorize("@el.check('user:list')")
    public void exportUser(HttpServletResponse response, UserQueryCriteria criteria) throws IOException {
        userService.download(userService.queryAll(criteria), response);
    }

    @ApiOperation("查询用户")
    @GetMapping
    @PreAuthorize("@el.check('user:list')")
    public ResponseEntity<Object> queryUser(UserQueryCriteria criteria, Pageable pageable) {
        if (!ObjectUtils.isEmpty(criteria.getDeptId())) {
            criteria.getDeptIds().add(criteria.getDeptId());
            // 先查找是否存在子节点
            List<Dept> data = deptService.findByPid(criteria.getDeptId());
            // 然后把子节点的ID都加入到集合中
            criteria.getDeptIds().addAll(deptService.getDeptChildren(data));
        }
        // 数据权限
        List<Long> dataScopes = dataService.getDeptIds(userService.findByName(JwtSecurityUtils.getCurrentUsername()));
        // criteria.getDeptIds() 不为空并且数据权限不为空则取交集
        if (!CollectionUtils.isEmpty(criteria.getDeptIds()) && !CollectionUtils.isEmpty(dataScopes)) {
            // 取交集
            criteria.getDeptIds().retainAll(dataScopes);
            if (!CollectionUtil.isEmpty(criteria.getDeptIds())) {
                return new ResponseEntity<>(userService.queryAll(criteria, pageable), HttpStatus.OK);
            }
        } else {
            // 否则取并集
            criteria.getDeptIds().addAll(dataScopes);
            return new ResponseEntity<>(userService.queryAll(criteria, pageable), HttpStatus.OK);
        }
        return new ResponseEntity<>(PageUtil.toPage(null, 0), HttpStatus.OK);
    }

    @Log("新增用户")
    @ApiOperation("新增用户")
    @PostMapping
    @PreAuthorize("@el.check('user:add')")
    public ResponseEntity<Object> createUser(@Validated @RequestBody User resources) {
        checkLevel(resources);
        // 默认密码 123456
        resources.setPassword(passwordEncoder.encode("123456"));
        userService.create(resources);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @Log("新增用户withOrgAreas")
    @ApiOperation("新增用户withOrgAreas")
    @PostMapping(value = "/addWithOrgAreas")
    @PreAuthorize("@el.check('user:add')")
    public ResponseEntity<Object> createUserWithOrgAreas(@Validated @RequestBody UserOrgDto dto) {
        checkLevel(dto);
        // 默认密码 123456
        //dto.setPassword(passwordEncoder.encode("123456"));
        if (dto.getPassword() == null) {
            throw new BadRequestException("请输入密码");
        }
        if (dto.getPassword().equals("")) {
            throw new BadRequestException("请输入密码");
        }
        dto.setPassword(passwordEncoder.encode(dto.getPassword()));
        userService.createWithOrgAreas(dto);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @Log("修改密码无需旧密码")
    @ApiOperation("修改密码无需旧密码")
    @PostMapping(value = "/updatePassWithOutOld")
    public ResponseEntity<Object> updatePassWithOutOld(@RequestBody UserPassWithOutOldVo passVo) throws Exception {
        Long userId = passVo.getUserId();
        UserDto dto = userService.findById(userId);
        if (dto == null) {
            throw new BadRequestException("未找到该用户，无法操作");
        }
        String pass = passVo.getPass();
        if (pass == null) {
            throw new BadRequestException("请输入密码");
        }
        if (pass.equals("")) {
            throw new BadRequestException("请输入密码");
        }
        userService.updatePass(dto.getUsername(), passwordEncoder.encode(pass));
        return new ResponseEntity<>(true, HttpStatus.OK);
    }

    @Log("修改用户")
    @ApiOperation("修改用户")
    @PutMapping
    @PreAuthorize("@el.check('user:edit')")
    public ResponseEntity<Object> updateUser(@Validated(User.Update.class) @RequestBody User resources) throws Exception {
        checkLevel(resources);
        userService.update(resources);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log("修改用户withOrgAreas")
    @ApiOperation("修改用户withOrgAreas")
    @PutMapping(value = "/editWithOrgAreas")
    @PreAuthorize("@el.check('user:edit')")
    public ResponseEntity<Object> updateUserWithOrgAreas(@Validated(User.Update.class) @RequestBody UserOrgDto dto) throws Exception {
        checkLevel(dto);
        userService.updateWithOrgAreas(dto);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log("修改用户：个人中心")
    @ApiOperation("修改用户：个人中心")
    @PutMapping(value = "center")
    public ResponseEntity<Object> centerUser(@Validated(User.Update.class) @RequestBody User resources) {
        if (!resources.getId().equals(JwtSecurityUtils.getCurrentUserId())) {
            throw new BadRequestException("不能修改他人资料");
        }
        userService.updateCenter(resources);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log("修改手机号发送验证码")
    @ApiOperation("修改手机号发送验证码")
    @PostMapping(value = "sendRandomCodeChangePhone")
    public ResponseEntity<Object> sendRandomCodeChangePhone(@RequestBody UserPhoneDto userPhoneDto) {
        smsRandomCodeService.sendRandomCodeChangePhone(userPhoneDto);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("根据短信验证码修改登录用户手机号")
    @ApiOperation("根据短信验证码修改登录用户手机号")
    @PutMapping(value = "updatePhoneBycode")
    public ResponseEntity<Object> updatePhoneBycode(@RequestBody UserPhoneWithRandomCodeDto userPhoneWithRandomCodeDto) {
        userService.updateUserPhoneBycode(userPhoneWithRandomCodeDto);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log("修改用户：微信")
    @ApiOperation("修改用户：微信")
    @PostMapping(value = "updateByWx")
    public ResponseEntity<Object> updateByWx(
            @RequestParam(value = "nickname", required = false) String nickname,
            @RequestParam(value = "avatar", required = false) MultipartFile file
    ) {
        if (file != null && !file.isEmpty()) {
            userService.updateAvatar(FileUtil.toFile(file));
        }
        if (nickname != null && !nickname.isEmpty()) {
            userService.updateNickname(SecurityUtils.getCurrentUsername(), nickname);
        }
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log("删除用户")
    @ApiOperation("删除用户")
    @DeleteMapping
    @PreAuthorize("@el.check('user:del')")
    public ResponseEntity<Object> deleteUser(@RequestBody Set<Long> ids) {
        for (Long id : ids) {
            Integer currentLevel = Collections.min(roleService.findByUsersId(JwtSecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
            Integer optLevel = Collections.min(roleService.findByUsersId(id).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
            if (currentLevel > optLevel) {
                throw new BadRequestException("角色权限不足，不能删除：" + userService.findById(id).getUsername());
            }
        }
        userService.delete(ids);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @ApiOperation("修改密码")
    @PostMapping(value = "/updatePass")
    public ResponseEntity<Object> updateUserPass(@RequestBody UserPassVo passVo) throws Exception {
        String oldPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, passVo.getOldPass());
        String newPass = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, passVo.getNewPass());
        //UserDto user = userService.findByName(JwtSecurityUtils.getCurrentUsername());
        UserDto user = userService.findsByName(JwtSecurityUtils.getCurrentUsername());
        if (!passwordEncoder.matches(oldPass, user.getPassword())) {
            throw new BadRequestException("修改失败，旧密码错误");
        }
        if (passwordEncoder.matches(newPass, user.getPassword())) {
            throw new BadRequestException("新密码不能与旧密码相同");
        }
        userService.updatePass(user.getUsername(), passwordEncoder.encode(newPass));
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @ApiOperation("修改头像")
    @PostMapping(value = "/updateAvatar")
    public ResponseEntity<Object> updateUserAvatar(@RequestParam MultipartFile avatar) {
        return new ResponseEntity<>(userService.updateAvatar(FileUtil.toFile(avatar)), HttpStatus.OK);
    }

    /*@Log("修改邮箱")
    @ApiOperation("修改邮箱")
    @PostMapping(value = "/updateEmail/{code}")
    public ResponseEntity<Object> updateUserEmail(@PathVariable String code, @RequestBody User user) throws Exception {
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, user.getPassword());
        UserDto userDto = userService.findByName(JwtSecurityUtils.getCurrentUsername());
        if (!passwordEncoder.matches(password, userDto.getPassword())) {
            throw new BadRequestException("密码错误");
        }
        verificationCodeService.validated(CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey() + user.getEmail(), code);
        userService.updateEmail(userDto.getUsername(), user.getEmail());
        return new ResponseEntity<>(HttpStatus.OK);
    }*/

    /**
     * 如果当前用户的角色级别低于创建用户的角色级别，则抛出权限不足的错误
     *
     * @param resources /
     */
    private void checkLevel(User resources) {
        Integer currentLevel = Collections.min(roleService.findByUsersId(JwtSecurityUtils.getCurrentUserId()).stream().map(RoleSmallDto::getLevel).collect(Collectors.toList()));
        Integer optLevel = roleService.findByRoles(resources.getRoles());
        if (currentLevel > optLevel) {
            throw new BadRequestException("角色权限不足");
        }
    }


    @Log("修改手机号")
    @ApiOperation("修改手机号")
    @GetMapping(value = "/updatePhone/{phone}")
    public ResponseEntity<Object> updatePhone(@PathVariable String phone) {
        userService.updateMyPhone(phone);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("检查是否填写手机号")
    @ApiOperation("检查是否填写手机号")
    @GetMapping(value = "/isPhone")
    public ResponseEntity<Object> isPhone() {
        Boolean flag = userService.isPhone();
        return new ResponseEntity<>(flag, HttpStatus.OK);
    }

    @Log("通过验证码注册用户")
    @ApiOperation("通过验证码注册用户")
    @AnonymousPostMapping(value = "/registByCode")
    public ResponseEntity<Object> registByCode(@Validated @RequestBody AuthRegistUserDto authRegistUserDto) {
        userService.registByCode(authRegistUserDto);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @Log("新通过验证码注册用户")
    @ApiOperation("新通过验证码注册用户")
    @AnonymousPostMapping(value = "/newRegistByCode")
    public ResponseEntity<Object> newRegistByCode(@Validated @RequestBody AuthNewRegistUserDto dto) {
        userService.registNewByCode(dto);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @Log("新用户注册发送验证码")
    @ApiOperation("新用户注册发送验证码")
    @AnonymousPostMapping(value = "/newSendRegistRandomCode")
    public ResponseEntity<Object> newSendRegistRandomCode(@RequestBody UserNewPhoneDto dto) {
        smsRegisterRandomCodeService.newSendRegistRandomCode(dto);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("用户注册发送验证码")
    @ApiOperation("用户注册发送验证码")
    @AnonymousPostMapping(value = "/sendRegistRandomCode")
    public ResponseEntity<Object> sendRegistRandomCode(@RequestBody UserPhoneDto userPhoneDto) {
        smsRegisterRandomCodeService.sendRegistRandomCode(userPhoneDto);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("检查是否为能看到所有市区最大权限")
    @ApiOperation("检查是否为能看到所有市区最大权限")
    @GetMapping(value = "/isAll")
    public ResponseEntity<Object> isAll() {
        Boolean flag = userService.isUserSeeAllCity();
        return new ResponseEntity<>(flag, HttpStatus.OK);
    }

    @Log("修密")
    @ApiOperation("修密")
    @AnonymousGetMapping(value = "/encodePwd")
    public ResponseEntity<Object> encodePwd(@RequestParam String code, @RequestParam String uname) {
        userService.updatePass(uname, passwordEncoder.encode(code));
        return new ResponseEntity<>(true, HttpStatus.OK);
    }

    @Log("修用户名")
    @ApiOperation("修用户名")
    @AnonymousGetMapping(value = "/editMyName")
    public ResponseEntity<Object> editMyName(@RequestParam String name, @RequestParam String id) {
        userService.updateMyUserName(name, id);
        return new ResponseEntity<>(true, HttpStatus.OK);
    }

    @Log("修改昵称、头像")
    @ApiOperation("修改昵称、头像")
    @PostMapping(value = "/editNickNameOrAvatar")
    public ResponseEntity<Object> editNickNameOrAvatar(@RequestBody UserPicNickNameDto dto) {
        userService.editNickNameOrAvatar(dto);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log("获取当前登录用户信息")
    @ApiOperation("获取当前登录用户信息")
    @GetMapping(value = "/getMyInfo")
    public ResponseEntity<Object> getMyInfo() {
        return new ResponseEntity<>(userService.getMyInfo(), HttpStatus.OK);
    }


    @Log("忘记密码发送验证码")
    @ApiOperation("忘记密码发送验证码")
    @AnonymousPostMapping(value = "/sendForgetRandomCode")
    public ResponseEntity<Object> sendForgetRandomCode(@RequestBody UserNewPhoneDto dto) {
        smsRegisterRandomCodeService.sendForgetRandomCode(dto);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log("忘记密码根据验证码重置密码")
    @ApiOperation("忘记密码根据验证码重置密码")
    @AnonymousPostMapping(value = "/forgetRandomCode")
    public ResponseEntity<Object> forgetRandomCode(@Validated @RequestBody AuthRegistForgetUserDto dto) {
        String code = dto.getCode();
        String phone = dto.getPhone();
        if (phone == null || phone.equals("")) {
            throw new BadRequestException("未找到手机号");
        }
        String pas = dto.getPassword();
        if (pas == null || pas.equals("")) {
            throw new BadRequestException("密码未填写");
        }
        //String pwd = passwordEncoder.encode(pas);
        if (code == null || code.equals("")) {
            throw new BadRequestException("验证码未找到");
        }
        Object res = redisUtils.get("forget" + phone);
        if (res == null) {
            throw new BadRequestException("验证码过期");
        }
        String redisCode = res.toString();
        if (!redisCode.equals(code)) {
            throw new BadRequestException("验证码不匹配");
        }
        UserDto userDto = userService.findsByName(phone);
        if (userDto == null) {
            throw new BadRequestException("未找到该用户，无法操作");
        }
        userService.updatePass(userDto.getUsername(), passwordEncoder.encode(pas));
        return new ResponseEntity<>(true, HttpStatus.OK);
    }
}
